/*LICENSE_BEGIN
 * ******************************************************************
 * Copyright 2006 by the Dainamite Robocup-Team at the
 * DAI-Labor, TU-Berlin (www.dai-labor.de)
 * 
 * Members of Dainamite are:
 * Holger Endert
 * Robert Wetzker
 * Thomas Karbe
 * Axel Heßler
 * Philippe Bütter
 * Felix Brossmann
 * 
 * Licensed under the GNU GPL (General Public License) Free Software 
 * License, Version 2. You may use this file only in compliance with 
 * the License. You may obtain a copy of the License at:
 * 
 * http://www.fsf.org/licensing/licenses/gpl.txt
 * ******************************************************************
LICENSE_END*/
package robocup.component.worldobjects;

import robocup.component.geometry.Vektor;

/**
 * Super-class for all movable objects, i.e. the player or a ball.
 * Therefore it extends the StaticObject by speed.
 */
public abstract class DynamicObject extends StaticObject {

	/**
	 * The speed of the object.
	 */
	protected Vektor speed = new Vektor();

	/**
	 * The last seen position of the object.
	 */
	protected Vektor lastSeenPosition = new Vektor();

	/**
	 * The cycle, in which the object was seen last.
	 */
	protected int lastSeenBodyCycle = -1;

	/**
	 * Is the position of the object reliable?
	 */
	private boolean isReliable = true;

	/**
	 * Is set to false, if the object is known to be non-existant.
	 * This may only occur in training.
	 */
	private boolean exists = true;

	// reliability of the object speed. true if speed calculation is
	// based on dist/dirchange, false if not.
	private boolean speedReliable = true;

	/**
	 * A moveable abject
	 * 
	 * @param iCycle -
	 *            time, when seen last (real cycle)
	 * @param bodyCycle -
	 *            time, when seen last (body-cycle)
	 * @param iPosAbs -
	 *            the absolute position
	 * @param iSpeed -
	 *            the speed
	 */
	public DynamicObject(int iCycle, int bodyCycle, Vektor iPosAbs,
			Vektor iSpeed) {
		super(iCycle, bodyCycle, iPosAbs);
		this.speed = iSpeed;
	}

	/**
	 * Extrapolates dynamic Objects based on their speed.
	 * 
	 * @param n -
	 *            horizont (cycles)
	 * @param decay
	 * 
	 * @author rw
	 */
	public void extrapolate(int n, double decay) {
		// as profiling shows that Math.pow is quiet slow
		// the common case of extrapoling by 1 cycle
		// is handled in a more efficient way
		if (n == 0)
			return;
		if (n == 1) {
			this.position.x += this.speed.x;
			this.position.y += this.speed.y;
			this.speed.x *= decay;
			this.speed.y *= decay;
		} else {
			this.position.x += (decay == 1) ? this.speed.x * n : this.speed.x
					* (1.0 - Math.pow(decay, n)) / (1.0 - decay);
			this.position.y += (decay == 1) ? this.speed.y * n : this.speed.y
					* (1.0 - Math.pow(decay, n)) / (1.0 - decay);
			this.speed.x *= Math.pow(decay, n);
			this.speed.y *= Math.pow(decay, n);
		}
		// no stamina update as the main actions are missing
	}

	/**
	 * The objects position after a given number of cycles.
	 * 
	 * @param cycles -
	 *            number of future cycles
	 * @param decay -
	 *            objects decay
	 * @return position - the future position
	 */
	public Vektor getFuturePos(int cycles, double decay) {
		Vektor newPos = this.getPosition().cloned();
		Vektor movement = this.speed.cloned();
		if (decay != 1.0) // geom. Reihe
			movement.mult((1.0 - Math.pow(decay, cycles)) / (1.0 - decay));
		else
			movement.mult(cycles);
		newPos.addToThis(movement);
		return newPos;
	}

	/**
	 * The objects velocity after a given number of cycles.
	 * 
	 * @param cycles -
	 *            number of future cycles
	 * @param decay -
	 *            objects decay
	 * @return - the future velocity
	 */
	public Vektor getFutureVelocity(int cycles, double decay) {
		Vektor movement = this.speed.cloned();
		movement.mult(Math.pow(decay, cycles));
		return movement;
	}

	/**
	 * The final objects position if no acceleration occurs.
	 * 
	 * @param decay
	 * @return The final objects position if no acceleration occurs.
	 */
	public Vektor getFinalPosition(double decay) {
		if (decay >= 1 || decay < 0)
			throw new ArithmeticException(
					"Don't call 'DynamicObject.getFinalPosition(decay)' with decay >=1 or decay < 0!!");
		return new Vektor(Vektor.XY, this.position.x + this.speed.x * 1
				/ (1 - decay), this.position.y + this.speed.y * 1 / (1 - decay));
	}

	/**
	 * the age of an dynamic object. time in senseBody steps since the object
	 * was last seen
	 * 
	 * @param currentBodySenseCycle
	 * @return age
	 */
	public int getAge(int currentBodySenseCycle) {
		if (currentBodySenseCycle < this.getBodyCycle())
			System.out
					.println("DynamicObject.getAge called with wrong current Cycle. C-Object:"
							+ this.getBodyCycle()
							+ " C-Param:"
							+ currentBodySenseCycle);
		return currentBodySenseCycle - this.getBodyCycle();
	}

	/**
	 * @param lastSeenPosition
	 *            The lastSeenPosition to set.
	 */
	public void setLastSeenPosition(Vektor lastSeenPosition) {
		this.lastSeenPosition.x = lastSeenPosition.x;
		this.lastSeenPosition.y = lastSeenPosition.y;
	}

	/**
	 * @return Returns the lastSeenPosition.
	 */
	public Vektor getLastSeenPosition() {
		return this.lastSeenPosition;
	}

	/**
	 * DynamicObject is reliable.
	 * 
	 * @return true if we know information about this object for sure 
	 */
	public boolean isReliable() {
		return this.isReliable;
	}

	/**
	 * Set DynamicObject reliable (or not).
	 * 
	 * @param b
	 */
	public void setReliable(boolean b) {
		this.isReliable = b;
	}

	/**
	 * @return Returns the object's velocity. Note that if you perform
	 *         operations on this Vektor that change it, you need to clone it
	 *         first!
	 */
	public Vektor getSpeed() {
		if (this.speed == null)
			this.speed = new Vektor();
		return this.speed;
	}

	/**
	 * @param speed
	 *            The speed to set.
	 */
	public void setSpeed(Vektor speed) {
		this.speed.x = speed.x;
		this.speed.y = speed.y;
	}

	/**
	 * @return Returns the speedReliable.
	 */
	public final boolean speedIsReliable() {
		return this.speedReliable;
	}

	/**
	 * @param speedReliable
	 *            The speedReliable to set.
	 */
	public final void setSpeedReliable(boolean speedReliable) {
		this.speedReliable = speedReliable;
	}

	/**
	 * @return Returns the lastSeenBodyCycle.
	 */
	public int getLastSeenBodyCycle() {
		return this.lastSeenBodyCycle;
	}

	/**
	 * @param lastSeenBodyCycle
	 *            The lastSeenBodyCycle to set.
	 */
	public void setLastSeenBodyCycle(int lastSeenBodyCycle) {
		this.lastSeenBodyCycle = lastSeenBodyCycle;
	}

	/**
	 * This method checks, if an object exists.
	 * @return true, if existant
	 */
	public boolean exists() {
		return this.exists;
	}

	/**
	 * This method sets an object non-existant. Note that the default is
	 * true. The bal exists always, only players should be set to non-
	 * existant. Beware of the usage. Up to now, this was only used in 
	 * training.
	 * 
	 * @param exists -
	 *            false, if player is not existant
	 */
	public void setExists(boolean exists) {
		// set the position also away
		this.exists = exists;
		this.position.x -= 100;
	}
}
